
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits.hpp>

#include "pod_wrapper.hpp"

namespace protocol
{
	//  = bits::net_byte_order

	template<typename size_type,
		 typename char_type>
	struct native_string
	{
		size_type m_size;
		char_type *m_data;
	};

	template<typename ByteOrder>
	struct protocol_traits
	{
		typedef ByteOrder byte_order;

		typedef uint8_t uint8_type;
		typedef uint16_t uint16_type;
		typedef uint32_t uint32_type;
		typedef uint64_t uint64_type;
		typedef wrapper::string_wrapper<uint32_t, char, byte_order> string_type;
		//typedef native_string string_type;

		struct wrapper
		{
			typedef integer_wrapper<uint8_t, byte_order> uint8_type;
			typedef integer_wrapper<uint16_t, byte_order> uint16_type;
			typedef integer_wrapper<uint32_t, byte_order> uint32_type;
			typedef integer_wrapper<uint64_t, byte_order> uint64_type;
			typedef string_wrapper<uint32_t, char, byte_order> string_type;
		};

		template<typename T>
		std::size_t load(T &obj, const boost::asio::const_buffer &data);

		template<typename T>
		std::size_t save(const boost::asio::mutable_buffer &data, const T &obj);
	};

	template<typename ByteOrder>
	struct wrapper
	{
		typedef integer_wrapper<uint8_t, byte_order> uint8_type;
		typedef integer_wrapper<uint16_t, byte_order> uint16_type;
		typedef integer_wrapper<uint32_t, byte_order> uint32_type;
		typedef integer_wrapper<uint64_t, byte_order> uint64_type;
		typedef string_wrapper<uint32_t, char, byte_order> string_type;
	};
		
	template<typename ByteOrder>
	struct binary_oarchive
	{
		static bool is_saving = true;
		static bool is_loading = false;

		typedef ByteOrder byte_order;

		typedef binary_oarchive<ByteOrder> this_type;
		typedef std::size_t (* save_function_type)(this_type &);

		binary_oarchive(char *buffer, std::size_t size)
			: m_buffer(buffer)
			, m_size(size) {}

		template<typename T>
		std::size_t save(T &obj) {
			save_function_type t = &T::save<binary_oarchive>;
			obj.save(*this);
		}

		template<typename T>
		std::size_t save(T *obj, std::size_t count) {
			save_function_type t = &T::save<binary_oarchive>;
			obj.save(*this);
		}

		char *m_buffer;
		std::size_t m_size;
		// error_code &m_error;
	};

	// 字节序一致的版本
	template<typename Integer, typename ByteOrder>
	std::size_t
	binary_oarchive<ByteOrder>::save(
		const integer_wrapper<typename boost::enable_if<boost::is_integral<Integer>, Integer>::type, ByteOrder> *integer,
		std::size_t count)
	{
		typedef integer_wrapper<Integer, ByteOrder> type;
		const std::size_t bytes = sizeof(Integer) * count;
		// assert(m_size >= bytes);
		for(std::size_t i = 0; i < count; ++i)
		{
			//wrapper_cast<type*>(m_buffer)->set(obj->get());
			std::memcpy(m_buffer, obj, bytes); // 因为字节序一致，所以可以直接拷贝
		}
		m_buffer += bytes;
		m_size -= bytes;
		return bytes;
	}

	// 字节序不一致的版本
	template<typename Integer, typename ByteOrder>
	std::size_t
	binary_oarchive<ByteOrder>::save(
		const integer_wrapper<typename boost::enable_if<boost::is_integral<Integer>, Integer>::type, bits::reversed_byte_order<ByteOrder>::value> *integer,
		std::size_t count)
	{
		typedef integer_wrapper<Integer, bits::reversed_byte_order<ByteOrder>::value> type;
		const std::size_t bytes = sizeof(Integer) * count;
		assert(m_size >= bytes);
		for(std::size_t i = 0; i < count; ++i)
		{
			wrapper_cast<type*>(m_buffer)->set(integer->get());
		}
		m_buffer += bytes;
		m_size -= bytes;
		return bytes;
	}

	// native type
	template<typename Integer, typename ByteOrder>
	std::size_t
	binary_oarchive<ByteOrder>::save(
		typename boost::enable_if<boost::is_integral<Integer>, Integer>::type integer,
		std::size_t count)
	{
		typedef integer_wrapper<Integer, ByteOrder> type;
		const std::size_t bytes = sizeof(Integer) * count;
		assert(m_size >= bytes);
		for(std::size_t i = 0; i < count; ++i)
		{
			wrapper_cast<type*>(m_buffer)->set(integer);
		}
		m_buffer += bytes;
		m_size -= bytes;
		return bytes;
	}

	template<typename SizeType, typename CharType, typename ByteOrder>
	std::size_t
	binary_oarchive<ByteOrder>::save(
		const string_wrapper<SizeType, CharType, ByteOrder> *string
		std::size_t count)
	{
		typedef string_wrapper<SizeType, CharType, ByteOrder> type;
		const std::size_t bytes = string->bytes() * count;
		assert(m_size >= bytes);
		for(std::size_t i = 0; i < count; ++i)
		{
			wrapper_cast<type*>(m_buffer)->set(integer);
		}
		m_buffer += bytes;
		m_size -= bytes;
		return bytes;
	}

	template<typename ByteOrder>
	struct binary_iarchive
	{
	};

	// TODO: assert ==> throw ???
	
	template<typename ByteOrder>
	std::size_t protocol_traits<ByteOrder>::load(protocol_traits<ByteOrder>::uint8_type &integer,
						     const boost::asio::const_buffer &data)
	{
		assert(boost::asio::buffer_size(data) >= sizeof(protocol_traits<ByteOrder>::uint8_type));
		integer = reinterpret_cast<const protocol_traits<ByteOrder>::wrapper::uint8_type *>(boost::asio::buffer_cast<const char*>(data))->get();
		return sizeof(protocol_traits<ByteOrder>::uint8_type);
	}
		
	template<typename ByteOrder>
	std::size_t protocol_traits<ByteOrder>::load(protocol_traits<ByteOrder>::uint16_type &integer,
						     const boost::asio::const_buffer &data)
	{
		assert(boost::asio::buffer_size(data) >= sizeof(protocol_traits<ByteOrder>::uint16_type));
		integer = reinterpret_cast<const protocol_traits<ByteOrder>::wrapper::uint16_type *>(boost::asio::buffer_cast<const char*>(data))->get();
		return sizeof(protocol_traits<ByteOrder>::uint16_type);
	}
		
	template<typename ByteOrder>
	std::size_t protocol_traits<ByteOrder>::load(protocol_traits<ByteOrder>::uint32_type &integer,
						     const boost::asio::const_buffer &data)
	{
		assert(boost::asio::buffer_size(data) >= sizeof(protocol_traits<ByteOrder>::uint32_type));
		integer = reinterpret_cast<const protocol_traits<ByteOrder>::wrapper::uint32_type *>(boost::asio::buffer_cast<const char*>(data))->get();
		return sizeof(protocol_traits<ByteOrder>::uint32_type);
	}

	template<typename ByteOrder>
	std::size_t protocol_traits<ByteOrder>::load(protocol_traits<ByteOrder>::uint64_type &integer,
						     const boost::asio::const_buffer &data)
	{
		assert(boost::asio::buffer_size(data) >= sizeof(protocol_traits<ByteOrder>::uint64_type));
		integer = reinterpret_cast<const protocol_traits<ByteOrder>::wrapper::uint64_type *>(boost::asio::buffer_cast<const char*>(data))->get();
		return sizeof(protocol_traits<ByteOrder>::uint64_type);
	}


	template<typename ByteOrder>
	std::size_t protocol_traits<ByteOrder>::load(protocol_traits<ByteOrder>::string_type* &string,
						     const boost::asio::const_buffer &data)
	{
		assert(boost::asio::buffer_size(data) >= sizeof(protocol_traits<ByteOrder>::string_type::size_type));
		string = reinterpret_cast<const protocol_traits<ByteOrder>::wrapper::string_type *>(boost::asio::buffer_cast<const char*>(data));
		return string->bytes();
	}

	typedef protocol_traits<bits::net_byte_order> default_protocol_traits;
	struct header
	{
		typedef default_protocol_traits traits;
		traits::uint16_type m_magic;
		traits::uint8_type m_version;
		traits::uint16_type m_type;
		traits::uint32_type m_sequence_number;
		traits::uint32_type m_data_length;

		std::size_t load(const boost::asio::const_buffer &data) {
			boost::asio::const_buffer left = data;
			left += traits.load(m_magic, left);
			left += traits.load(m_version, left);
			left += traits.load(m_type, left);
			left += traits.load(m_sequence_number, left);
			left += traits.load(m_data_length, left);
			return boost::asio::buffer_size(data) - boost::asio::buffer_size(left);
		}

		template<typename OArchive>
		std::size_t save(const OArchive &out) {
			out << m_magic << ... ;
		}
	};

	namespace wrapper {
		
		class header_wrapper
		{
			integer_wrapper<uint16_t> m_magic;
			integer_wrapper<uint8_t> m_version;
			integer_wrapper<uint16_t> m_type;
			integer_wrapper<uint32_t> m_sequence_number;
			integer_wrapper<uint32_t> m_data_length;

			template<typename Archive>
			std::size_t load(Archive &ar) {
				
			}

			template<typename Archive>
			std::size_t save(Archive &ar) {
			}
		};

		class request_wrapper
			: public header_wrapper
		{
			integer_wrapper<uint32_t> m_program_version;
		};
		class response_wrapper
			: public header_wrapper
		{
			integer_wrapper<uint32_t> m_error_code;
		};

		struct root_meta_req_wrapper
			: public request_wrapper
		{
			tablet_info_wrapper m_tablet_info;
			server_address_wrapper m_server_address;
		}
		
	} // namespace wrapper
	
} // namespace protocol
